import "@babylonjs/core/Debug/debugLayer";
import "@babylonjs/inspector";
import "@babylonjs/loaders/glTF";
import { Engine, Scene, ArcRotateCamera, Vector3, HemisphericLight, Mesh, MeshBuilder, StandardMaterial, Color3, Texture, Vector4, Color4, TransformNode, DynamicTexture, Axis, Space, CubeTexture, Camera, SpriteManager, Sprite, ParticleSystem, PointerEventTypes, SpotLight, GroundMesh, DirectionalLight, ShadowGenerator } from "@babylonjs/core";


/*
 * @Author: Songmengyu
 * @Date: 2021-12-02 15:28:47
 * @LastEditors: Songmengyu
 * @LastEditTime: 2021-12-02 22:56:47
 * @Description: babylonjs 主入口
 */
class App {
    constructor() {
        // 创建画布html元素并将其附加到网页
        var canvas = document.createElement("canvas");
        canvas.style.width = "100%";
        canvas.style.height = "100%";
        canvas.id = "gameCanvas";
        document.body.appendChild(canvas);

        // 初始化Babylonjs场景和引擎
        var engine = new Engine(canvas, true);
        var scene = new Scene(engine);

      
        // var light1: HemisphericLight = new HemisphericLight("light1", new Vector3(1, 1, 0), scene);
        // light1.intensity = 0.1; // 光的强度

        // let faceUV = [];
        // faceUV[0] = new Vector4(0.5, 0.0, 0.75, 1.0); //rear face
        // faceUV[1] = new Vector4(0.0, 0.0, 0.25, 1.0); //front face
        // faceUV[2] = new Vector4(0.25, 0, 0.5, 1.0); //right side
        // faceUV[3] = new Vector4(0.75, 0, 1.0, 1.0); //left side

        var columns = 6;  // 6 columns
        var rows = 4;  // 4 rows
    
        var faceUV = new Array(6);
    
        for (var i = 0; i < 6; i++) {
            faceUV[i] = new Vector4(i / columns, 0, (i + 1) / columns, 1 / rows);
        }
        let options = {
            faceUV:faceUV, 
            wrap:true 
        };

        var box: Mesh = MeshBuilder.CreateBox("box", options , scene);
       
        const boxMat = new StandardMaterial("boxMat", scene);
        boxMat.diffuseTexture = new Texture("resource/spriteAtlas.png", scene);
        box.material = boxMat;
        box.position = new Vector3(3, 0, 0);

        // const material = new StandardMaterial("name", scene);
        // material.diffuseColor = new Color3(0, 1, 0);
        // var plane:Mesh = MeshBuilder.CreatePlane("plane", {width:10, height:10}, scene);
        // plane.material = material;

        let faceUV1 = [];
        faceUV1[0] = new Vector4(0, 0, 0, 0);
        faceUV1[1] = new Vector4(1, 0, 0.32, 1);
        faceUV1[2] = new Vector4(0, 0, 0.25, 1);
        
        var faceColors = [ ];
        faceColors[0] = new Color4(0.5, 0.5, 0.5, 1)
        
        var can = MeshBuilder.CreateCylinder("can", {height:1.16, faceUV: faceUV, faceColors: faceColors}, scene);
        can.position = new Vector3(0, 1, 0);
        can.material = boxMat;
        // 创建一个跟随人物的摄像机
        const camera = new ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 10, new Vector3(0, 0, 0), scene);
        camera.attachControl(canvas, true);

        // camera.parent = box;

        // 创建三维坐标系
        this.showAxis(6, scene);
        // 创建等腰三角形
        this.createLines(box, scene);
        // 创建地形图
        let largeGround = this.createMap(scene);
        // 默认支持四个材质（包括自己）可以加4个灯材质， 最大设为5
        (largeGround.material as StandardMaterial).maxSimultaneousLights = 5;
        // 创建天空盒
        this.createSkyBox(scene, camera);
        // 创建树林
        this.createTrees(scene);
        // 创建ufo动画
        this.createUFOAnimate(scene);
        // 创建一个喷泉
        let fountain = this.createFountain(scene);
        // 创建一个水粒子
        let particleSystem = this.createWaterParticle(scene);
        // 创建路灯
        let lamp = this.createLamp(scene);
        lamp.position = new Vector3(2, 0, 2); 
        lamp.rotation = Vector3.Zero();
        lamp.rotation.y = -Math.PI / 4;

        let lamp3 = lamp.clone("lamp3");
        lamp3.position.z = -8;

        let lamp1 = lamp.clone("lamp1");
        lamp1.position.x = -8;
        lamp1.position.z = 1.2;
        lamp1.rotation.y = Math.PI / 2;

        let lamp2 = lamp1.clone("lamp2");
        lamp2.position.x = -2.7;
        lamp2.position.z = 0.8;
        lamp2.rotation.y = -Math.PI / 2;
        
        // 创建一个平行光
        const  light = new DirectionalLight("dir01", new Vector3(0, -1, 1), scene);
        light.position = new Vector3(0, 15, -30);

        // 创建GUI
        // this.createGUI(scene, light);
        
        // 创建阴影
        const shadowGenerator = new ShadowGenerator(1024, light, false);
        shadowGenerator.addShadowCaster(fountain, true);
        // 地面接收阴影
        largeGround.receiveShadows = true;

       
        // 显示/隐藏 查看器
        window.addEventListener("keydown", (ev) => {
            // Shift+Ctrl+Alt+I
            if (ev.shiftKey && ev.ctrlKey && ev.altKey && ev.key === "I") {
                if (scene.debugLayer.isVisible()) {
                    scene.debugLayer.hide();
                } else {
                    scene.debugLayer.show();
                }
            }
        });

        let switched = false;
        let pointerDown = (mesh) => {
            if (mesh === fountain) {
                switched = !switched;
                if(switched) {
                    // Start the particle system
                    particleSystem.start();
                }
                else {
                    // Stop the particle system
                    particleSystem.stop();
                }
            }

        }

        scene.onPointerObservable.add((pointerInfo) => {      		
            switch (pointerInfo.type) {
                case PointerEventTypes.POINTERDOWN:
                    if(pointerInfo.pickInfo.hit) {
                        pointerDown(pointerInfo.pickInfo.pickedMesh)
                    }
                    break;
            }
        });

        // 渲染主循环
        engine.runRenderLoop(() => {
            scene.render();
        });
    }

   

    private createLamp(scene:Scene):Mesh {
        //创建网格灯柱和灯泡后，添加聚光灯和其他光源
        //然后将使柱成为灯泡的父级，并
        //把灯泡传给父母
        const lampLight = new SpotLight("lampLight", Vector3.Zero(), new Vector3(0, -1, 0), Math.PI, 1, scene);
        lampLight.diffuse = Color3.Yellow();

        // 要挤出的形状
        const lampShape = [];
        for(let i = 0; i < 20; i++) {
            lampShape.push(new Vector3(Math.cos(i * Math.PI / 10), Math.sin(i * Math.PI / 10), 0));
        }
        lampShape.push(lampShape[0]); //close shape

        // 挤出的路径
        const lampPath = [];
        lampPath.push(new Vector3(0, 0, 0));
        lampPath.push(new Vector3(0, 10, 0));
        for(let i = 0; i < 20; i++) {
            lampPath.push(new Vector3(1 + Math.cos(Math.PI - i * Math.PI / 40), 10 + Math.sin(Math.PI - i * Math.PI / 40), 0));
        }
        lampPath.push(new Vector3(3, 11, 0));

        const yellowMat = new StandardMaterial("yellowMat", scene);
        yellowMat.emissiveColor = Color3.Yellow();

        // 挤压灯
        const lamp = MeshBuilder.ExtrudeShape("lamp", {cap: Mesh.CAP_END, shape: lampShape, path: lampPath, scale: 0.5});
        
        // 添加灯泡
        const bulb = MeshBuilder.CreateSphere("bulb", {diameterX: 1.5, diameterZ: 0.8});
        bulb.material = yellowMat;
        bulb.parent = lamp;
        bulb.position.x = 2;
        bulb.position.y = 10.5;

        lampLight.parent = bulb;

        lamp.scaling = new Vector3(0.1, 0.1, 0.1);
        
        return lamp;

    }

    private createWaterParticle(scene:Scene):ParticleSystem {
        // 创建例子系统 
        var particleSystem = new ParticleSystem("particles", 5000, scene);
        // 每一个粒子用到的贴图
        particleSystem.particleTexture = new Texture("resource/flare.png", scene);
        particleSystem.emitter = new Vector3(1, 1, 1); // the starting object, the emitter
        particleSystem.minEmitBox = new Vector3(-0.1, 0, 0); // Starting all from
        particleSystem.maxEmitBox = new Vector3(0.1, 0, 0); // To...
    
        // 所有粒子的颜色
        particleSystem.color1 = new Color4(0.7, 0.8, 1.0, 1.0);
        particleSystem.color2 = new Color4(0.2, 0.5, 1.0, 1.0);
        particleSystem.colorDead = new Color4(0, 0, 0.2, 0.0);
    
        // 每个例子的尺寸 (random between...
        particleSystem.minSize = 0.1;
        particleSystem.maxSize = 0.5;
    
        // 每个例子的生命周期 (random between...
        particleSystem.minLifeTime = 2;
        particleSystem.maxLifeTime = 3.5;
    
        // 发射速率
        particleSystem.emitRate = 1500;
    
        // 混合模式: BLENDMODE_ONEONE, or BLENDMODE_STANDARD
        particleSystem.blendMode = ParticleSystem.BLENDMODE_ONEONE;
    
        // 设置所有粒子的重力
        particleSystem.gravity = new Vector3(0, -9.81, 0);
    
        // 每个粒子发射后的方向
        particleSystem.direction1 = new Vector3(-2, 8, 2);
        particleSystem.direction2 = new Vector3(2, 8, -2);
    
        // 角速度，以弧度为单位
        particleSystem.minAngularSpeed = 0;
        particleSystem.maxAngularSpeed = Math.PI;
    
        // 速度
        particleSystem.minEmitPower = 1;
        particleSystem.maxEmitPower = 3;
        particleSystem.updateSpeed = 0.025;
    
        // 启动例子系统
        // particleSystem.start();

        //添加点击事件控制开关

       return particleSystem;

       
    }

    private createFountain(scene:Scene):Mesh {
        let fountainProfile = [
            new Vector3(0, 0, 0),
            new Vector3(10, 0, 0),
            new Vector3(10, 4, 0),
            new Vector3(8, 4, 0),
            new Vector3(8, 1, 0),
            new Vector3(1, 2, 0),
            new Vector3(1, 15, 0),
            new Vector3(3, 17, 0)
        ];
        const fountain = MeshBuilder.CreateLathe("fountain", {
            shape: fountainProfile, 
            sideOrientation: Mesh.DOUBLESIDE
        }, scene);

        fountain.scaling = new Vector3(0.1, 0.1, 0.1);
        fountain.position = new Vector3(1, 0, 1);

        return fountain;
    }


    private createUFOAnimate(scene:Scene):void {
        const spriteManagerUFO = new SpriteManager("UFOManager","resource/ufo.png", 1, {
            width: 128, 
            height: 76
        }, scene);
        const ufo = new Sprite("ufo", spriteManagerUFO);
        ufo.playAnimation(0, 16, true, 125);
        ufo.position = new Vector3(0, 3, 0);
    }

    private createTrees(scene:Scene):void {
        // 创建精灵管理器
        const spriteManagerTrees = new SpriteManager("treesManager", "resource/palmtree.png", 2000, {width: 512, height: 1024}, scene);
        //We create trees at random positions
        for (let i = 0; i < 500; i++) {
            const tree = new Sprite("tree", spriteManagerTrees);
            tree.position.x = Math.random() * (-30);
            tree.position.z = Math.random() * 20 + 8;
            tree.position.y = 0.5;
        }

        for (let i = 0; i < 500; i++) {
            const tree = new Sprite("tree", spriteManagerTrees);
            tree.position.x = Math.random() * (25) + 7;
            tree.position.z = Math.random() * -35  + 8;
            tree.position.y = 0.5;
        }
    }

    private createSkyBox(scene:Scene, camera:ArcRotateCamera):void {

        let skybox = MeshBuilder.CreateBox("skyBox", {size:150}, scene);
        let skyboxMaterial = new StandardMaterial("skyBox", scene);
        skyboxMaterial.backFaceCulling = false;
        // 天空和需要用cubetexture 来创建反射贴图 reflectiontexyure
        skyboxMaterial.reflectionTexture = new CubeTexture("resource/skybox", scene);
        skyboxMaterial.reflectionTexture.coordinatesMode = Texture.SKYBOX_MODE;
        skyboxMaterial.diffuseColor = new Color3(0, 0, 0); // 漫反射颜色
        skyboxMaterial.specularColor = new Color3(0, 0, 0); // 高光/镜面反射颜色
        // 
        skybox.material = skyboxMaterial;
        camera.upperBetaLimit = Math.PI / 2.2;
    }

    private createMap(scene:Scene):GroundMesh {
        const largeGroundMat = new StandardMaterial("largeGroundMat", scene);
        largeGroundMat.diffuseTexture = new Texture("resource/map.jpg", scene);


        const largeGround = MeshBuilder.CreateGroundFromHeightMap("largeGround", "resource/villageheightmap.png", {
            width:150, 
            height:150, 
            subdivisions: 20, 
            minHeight:0, 
            maxHeight: 10
        }, scene);

        largeGround.material = largeGroundMat;
        largeGround.position.y = -0.1;

        return largeGround;
    }

    private createLines(sphere:Mesh, scene:Scene):void {
        const points = [];
        points.push(new Vector3(2, 0, 2));
        points.push(new Vector3(2, 0, -2));
        points.push(new Vector3(-2, 0, -2));
        points.push(points[0]); //close the triangle;
    
        MeshBuilder.CreateLines("triangle", {points: points}, scene);

        const slide = function (turn:number, dist:number) { //after covering dist apply turn
            this.turn = turn;
            this.dist = dist;
        }
        
        const track = [];
        track.push(new slide(Math.PI / 2, 4));
        track.push(new slide(3 * Math.PI / 4, 8));
        track.push(new slide(3 * Math.PI / 4, 8 + 4 * Math.sqrt(2)));
    
        let distance = 0;
        let step = 0.05;
        let p = 0;
    
        scene.onBeforeRenderObservable.add(() => {
            sphere.movePOV(0, 0, step);
            distance += step;
                  
            if (distance > track[p].dist) {        
                sphere.rotate(Axis.Y, track[p].turn, Space.LOCAL);
                p +=1;
                p %= track.length;
                if (p === 0) {
                    distance = 0;
                    sphere.position = new Vector3(2, 0, 2);     //reset to initial conditions
                    sphere.rotation = Vector3.Zero();   //prevents error accumulation
                }
            }
        });
    }

    showAxis(size, scene):void {
        const makeTextPlane = (text, color, size) => {
            const dynamicTexture = new DynamicTexture("DynamicTexture", 50, scene, true);
            dynamicTexture.hasAlpha = true;
            dynamicTexture.drawText(text, 5, 40, "bold 36px Arial", color , "transparent", true);
            const plane = Mesh.CreatePlane("TextPlane", size, scene, true);
            plane.material = new StandardMaterial("TextPlaneMaterial", scene);
            plane.material.backFaceCulling = false;
            (plane.material as StandardMaterial).specularColor = new Color3(0, 0, 0);
            (plane.material as StandardMaterial).diffuseTexture = dynamicTexture;
            return plane;
        };
      
        const axisX = Mesh.CreateLines("axisX", [ 
            Vector3.Zero(), new Vector3(size, 0, 0), new Vector3(size * 0.95, 0.05 * size, 0), 
            new Vector3(size, 0, 0), new Vector3(size * 0.95, -0.05 * size, 0)
        ]);
        axisX.color = new Color3(1, 0, 0);
        const xChar = makeTextPlane("X", "red", size / 10);
        xChar.position = new Vector3(0.9 * size, -0.05 * size, 0);
    
        const axisY = Mesh.CreateLines("axisY", [
            Vector3.Zero(), new Vector3(0, size, 0), new Vector3( -0.05 * size, size * 0.95, 0), 
            new Vector3(0, size, 0), new Vector3( 0.05 * size, size * 0.95, 0)
        ]);
        axisY.color = new Color3(0, 1, 0);
        const yChar = makeTextPlane("Y", "green", size / 10);
        yChar.position = new Vector3(0, 0.9 * size, -0.05 * size);
        
        const axisZ = Mesh.CreateLines("axisZ", [
            Vector3.Zero(), new Vector3(0, 0, size), new Vector3( 0 , -0.05 * size, size * 0.95),
            new Vector3(0, 0, size), new Vector3( 0, 0.05 * size, size * 0.95)
        ]); 
        axisZ.color = new Color3(0, 0, 1);
        const zChar = makeTextPlane("Z", "blue", size / 10);
        zChar.position = new Vector3(0, 0.05 * size, 0.9 * size);
    };
}
new App();